#pragma once
#include <base_definition.h>
#include <boot_ld.h>
#include <config.h>

static inline noreturn void die(void);
static inline u32 diff(u32 x, u32 y);
static inline void _memset(void *memptr, u8 pad, u32 len);
static inline void _memcpy(ptr src, ptr des, u32 len);
static inline void *alloc(u32 size, u32 align);

#define memset _memset
#define memcpy _memcpy
#define init(obj, size) memset(&(obj), 0, size);

void die(void)
{
loop:
    asm volatile("hlt");
    goto loop;
}

u32 diff(u32 x, u32 y)
{
    return x > y ? x - y : y - x;
}

void *alloc(u32 size, u32 align)
{
    u32 pos = alloc_pos;
    pos = ((pos + align - 1) & (~(align - 1)));
    alloc_pos = pos + size;
    return (void *)pos;
}

#if ARCH == i8086
struct packed DAP
{
    u8 size;
    u8 reserved;
    u16 number_of_sector;
    u16 offset;
    u16 segment;
    u64 start_sector_absolute;
};

static inline void print(const char *str);
static inline void panic(const char *msg);
static inline result diskRead(u16 number_of_sector, u32 offset, u32 start);
static inline void set_es(u16 es);

#define printu(n)                              \
    void print_u##n(u##n num)                  \
    {                                          \
        char c[(n / 4) + 1];                   \
        for (i16 i = (n / 4) - 1; i >= 0; i--) \
        {                                      \
            u8 t = num % 16;                   \
            num /= 16;                         \
            if (0 <= t && t < 10)              \
                c[i] = '0' + t;                \
            else                               \
                c[i] = 'A' + t - 10;           \
        }                                      \
        c[(n / 4)] = 0;                        \
        print(c);                              \
    }
static inline printu(64);
static inline printu(32);
static inline printu(16);
static inline printu(8);
#undef printu

#define println(str) print(str "\r\n")
#define checkpoint()         \
    {                        \
        print(__FILE__);     \
        print(":");          \
        print(__FUNCTION__); \
        print(":");          \
        print_u32(__LINE__); \
        println();           \
    }

void _memset(void *memptr, u8 pad, u32 len)
{
    ptr pmemptr = (ptr)memptr;
    asm volatile(
        "addr32 rep stosb ;"
        : "+D"(pmemptr), "+c"(len)
        : "a"(pad) /* XXX: 确保位于%al? */
        : "memory");
}

void _memcpy(ptr src, ptr des, u32 len)
{
    ptr psrc = src, pdes = des;
    asm volatile(
        "addr32 rep movsb ;"
        : "+S"(psrc), "+D"(pdes), "+c"(len)::"memory");
}

void print(const char *str)
{
#ifdef PRINT_TO_SERIAL
    for (u16 i = 0; str[i]; i++)
    {
        u16 r = 0x0100 + str[i];
        asm volatile(
            "int $0x14"
            : "+a"(r)
            : "d"(0));
    }
#endif
    ptr pstr = (ptr)str;
    asm volatile(
        "calll	eprint ;"
        : "+S"(pstr));
}

void panic(const char *msg)
{
    print(msg);
    die();
}

result diskRead(u16 number_of_sector, u32 offset, u32 start)
{
    struct DAP dap;
    init(dap, sizeof(dap));
    u8 buffer[512];
    dap.size = 0x10;
    dap.reserved = 0;
    dap.number_of_sector = 1;
    dap.offset = (u16)(ptr)buffer;
    dap.segment = 0;
    dap.start_sector_absolute = start;

    for (int i = 0; i < number_of_sector; i++)
    {
        u8 r;
        u32 _;
        asm volatile("movb	$0, %0 ; "
                     "movb	$0x42, %%ah ; "
                     "movb	diskid, %%dl ; "
                     "clc ; "
                     "int	$0x13 ; "
                     "jnc	1f ; "
                     "movb	%%ah, %0 ; \n"
                     "1:"
                     : "=qQ"(r)
                     : "S"((ptr)&dap)
                     : "%ah", "%dl", "flags", "memory");
        if (r != 0)
            return ERROR;
        asm volatile("addr32 rep movsb"
                     : "=S"(_), "+D"(offset)
                     : "S"((u32)buffer), "c"((u32)512)
                     : "memory");
        dap.start_sector_absolute++;
    }
    return SUCCESS;
};

void set_es(u16 es)
{
    asm volatile("movw %0, %%es" ::"R"(es));
}

#elif ARCH == i386

void _memset(void *memptr, u8 pad, u32 len)
{
    ptr pmemptr = (ptr)memptr;
    asm volatile(
        "rep stosb ;"
        : "+D"(pmemptr), "+c"(len)
        : "a"(pad) /* XXX: 确保位于%al? */
        : "memory");
}

void _memcpy(ptr src, ptr des, u32 len)
{
    ptr psrc = src, pdes = des;
    asm volatile(
        "rep movsb ;"
        : "+S"(psrc), "+D"(pdes), "+c"(len)::"memory");
}
#else
#error "Wrong ARCH defined"
#endif
